package Connections;

import Gear.Factory;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.file.FileSystems;


/**
 * Principal client class which implements all crucial methods.
 *
 * @author Rodrigo Marins Racanicci
 */
public class Client {

    /**
     * Client socket.
     */
    private Socket client;

    /**
     * Client input stream.
     */
    private InputStream input;

    /**
     * Client output stream.
     */
    private OutputStream output;

    /**
     * Client input stream reader.
     */
    private InputStreamReader inputStreamReader;

    /**
     * Client output stream writer.
     */
    private OutputStreamWriter outputStreamWriter;

    /**
     * Client buffered reader.
     */
    private BufferedReader inputBufferedReader;

    /**
     * Client buffered writer.
     */
    private BufferedWriter outputBufferedWriter;

    /**
     * Client constructor.
     * Connect to tracker.
     */
    public Client() {
        try {
            client = new Socket(InetAddress.getByName(Factory.getInstance().getSettings().getServerHost()), Factory.getInstance().getSettings().getServerPort());

            input = client.getInputStream();
            output = client.getOutputStream();
            inputStreamReader = new InputStreamReader(input);
            outputStreamWriter = new OutputStreamWriter(output);
            inputBufferedReader = new BufferedReader(inputStreamReader);
            outputBufferedWriter = new BufferedWriter(outputStreamWriter);

        } catch (UnknownHostException ex) {
            System.out.println("CLIENT: invalid server to connect");
            throw new RuntimeException();
        } catch (IOException ex) {
            System.out.println("CLIENT: error creating client");
        }
    }

    /**
     * Client constructor.
     * Connect to a specified host.
     *
     * @param serverHost Server host to connect.
     */
    public Client(String serverHost) {
        try {
            client = new Socket(InetAddress.getByName(serverHost), Factory.getInstance().getSettings().getServerPort());

            input = client.getInputStream();
            output = client.getOutputStream();
            inputStreamReader = new InputStreamReader(input);
            outputStreamWriter = new OutputStreamWriter(output);
            inputBufferedReader = new BufferedReader(inputStreamReader);
            outputBufferedWriter = new BufferedWriter(outputStreamWriter);

        } catch (UnknownHostException ex) {
            System.out.println("CLIENT: invalid server to connect");
        } catch (IOException ex) {
            System.out.println("CLIENT: error creating client");
        }
    }

    /**
     * Close client connection.
     */
    public void close() {
        try {
            input.close();
            output.close();
            inputStreamReader.close();
            outputStreamWriter.close();
            inputBufferedReader.close();
            outputBufferedWriter.close();
        } catch (IOException ex) {
            System.out.println("CLIENT: error closing client");
        }
    }

    /**
     * Send a command to the server.
     *
     * @param command Command to send.
     */
    public void sendCommand(String command) {
        try {
            outputBufferedWriter.write(command + "\n");
            outputBufferedWriter.flush();

            //System.out.println("CLIENT: command " + command + " sent");

        } catch (IOException ex) {
            System.out.println("CLIENT: error sending command");
        }
    }

    /**
     * Receive a command from server.
     *
     * @return Received command.
     */
    public String receiveCommand() {
        try {
            String command = inputBufferedReader.readLine();

            //System.out.println("CLIENT: command " + command + " received");


            return command;
        } catch (IOException ex) {
            System.out.println("CLIENT: error receiving command");
            return null;
        }
    }

    /**
     * Send a file to the server.
     *
     * @param source File to send.
     */
    public void sendFile(File source) {

        int bytesRead;
        int bufferSize;
        byte[] buffer;
        FileInputStream inputStream;

        try {
            inputStream = new FileInputStream(source);

            // Enviar nome
            outputBufferedWriter.write(source.getName() + "\n");
            outputBufferedWriter.flush();

            // Buffer de envio
            bufferSize = Factory.getInstance().getSettings().getBufferSize();
            buffer = new byte[bufferSize];

            //System.out.println("CLIENT: Sending " + source);

            // Envio bufferizado
            while ((bytesRead = inputStream.read(buffer, 0, bufferSize)) != -1) {
                output.write(buffer, 0, bytesRead);
                output.flush();
            }

            inputStream.close();
            this.close();
            //System.out.println("CLIENT: Sent " + source + " and client closed");

        } catch (FileNotFoundException ex) {
            System.out.println("CLIENT: Invalid file " + source);
        } catch (IOException ex) {
            System.out.println("CLIENT: Error on socket manipulation" + source);
        }
    }

    /**
     * Receive a file of the server.
     *
     * @param sourceName File name
     *
     * @return File received.
     */
    public File receiveFile(String sourceName) {

        int bytesRead;
        int bufferSize;
        byte[] buffer;
        FileOutputStream outputStrem;
        File target;

        try {
            target = FileSystems.getDefault().getPath(Factory.getInstance().getSettings().getGearPath().toString(), sourceName).toFile();
            outputStrem = new FileOutputStream(target);

            //System.out.println("CLIENT: Receiving " + target);

            bufferSize = Factory.getInstance().getSettings().getBufferSize();
            buffer = new byte[bufferSize];

            while ((bytesRead = input.read(buffer, 0, bufferSize)) != -1) {
                outputStrem.write(buffer, 0, bytesRead);
                outputStrem.flush();
            }

            outputStrem.flush();
            outputStrem.close();
            this.close();
            //System.out.println("CLIENT: Received " + target + " and client closed");

            return target;

        } catch (FileNotFoundException ex) {
            System.out.println("CLIENT: Invalid file");
            return null;
        } catch (IOException ex) {
            System.out.println("CLIENT: Error on receiving file");
            return null;
        }
    }
}